#include "workCmd.h"
static void trans_rwx(mode_t mode,char *mode_str);
static void trans_time(time_t mtime,char *time_str);
static int compare(const void *a,const void *b);

void initPwdStack(client_t *pclient, const char *base){
    pclient->stackTop = 0;
    char path[1024];
    strncpy(path, base, sizeof(path));
    char *token = strtok(path, "/");
    while (token && pclient->stackTop < 64) {
        pclient->pwdStack[pclient->stackTop++] = strdup(token);
        token = strtok(NULL, "/");
    }
}
void getPwdFromStack(client_t *pclient, char *outPath, size_t len){
    strcpy(outPath, "/");
    for (int i = 0; i < pclient->stackTop; ++i) {
        strcat(outPath, pclient->pwdStack[i]);
        if (i != pclient->stackTop - 1) strcat(outPath, "/");
    }
}
void destroyPwdStack(client_t *pclient){
    for (int i = 0; i < pclient->stackTop; ++i) {
        free(pclient->pwdStack[i]);
    }
    pclient->stackTop = 0;
}

int doCd(client_t *pclient, const char *targetPath) {
    // 模拟压栈
    char *tempStack[64] = {0};
    int tempTop = pclient->stackTop;
    for (int i = 0; i < tempTop; ++i) {
        tempStack[i] = strdup(pclient->pwdStack[i]);
    }

    if (targetPath[0] == '/') {
        for (int i = 0; i < tempTop; ++i) free(tempStack[i]);
        tempTop = 0;
    }

    char path[1024];
    strncpy(path, targetPath, sizeof(path));
    char *token = strtok(path, "/");
    while (token && tempTop < 64) {
        if (strcmp(token, "..") == 0) {
            if (tempTop > 0) free(tempStack[--tempTop]);
        } else if (strcmp(token, ".") != 0) {
            tempStack[tempTop++] = strdup(token);
        }
        token = strtok(NULL, "/");
    }

    // 构造绝对路径用于 access
    char absPath[1024] = "/";
    for (int i = 0; i < tempTop; ++i) {
        strcat(absPath, tempStack[i]);
        if (i != tempTop - 1) strcat(absPath, "/");
    }

    train_t train;
    if (access(absPath, F_OK) == -1) {
        snprintf(train.data, sizeof(train.data), "cd failed: %s", strerror(errno));
    } else {
        destroyPwdStack(pclient);
        for (int i = 0; i < tempTop; ++i) {
            pclient->pwdStack[i] = tempStack[i];
        }
        pclient->stackTop = tempTop;
        snprintf(train.data, sizeof(train.data), "cd success");
    }

    train.length = strlen(train.data);
    send(pclient->fd, &train.length, sizeof(train.length), MSG_NOSIGNAL);
    send(pclient->fd, train.data, train.length, MSG_NOSIGNAL);
    return 0;
}

int doRmdir(client_t* pclient,const char *targetName){
    train_t train;
    char absPath[1024] = {0};
    getPwdFromStack(pclient, absPath, sizeof(absPath));

    // 拼接目标路径
    char fullPath[1024] = {0};
    snprintf(fullPath, sizeof(fullPath), "%s/%s", absPath, targetName);

    int ret = rmdir(fullPath);
    if (ret == -1) {
        perror("rmdir fail");
        snprintf(train.data, sizeof(train.data), "rmdir failed: %s", strerror(errno));
    }else{strcpy(train.data, "rmdir success");}
    train.length = strlen(train.data);
    send(pclient->fd, &train.length, sizeof(train.length), MSG_NOSIGNAL);
    send(pclient->fd, train.data, train.length, MSG_NOSIGNAL);

    return 0;
}

int doLs(client_t *pclient){
    char absPath[1024]={0};
    getPwdFromStack(pclient,absPath,sizeof(absPath));
    DIR *dir = opendir(absPath);
    ERROR_CHECK(dir,NULL,"opendir");

    train_t train;
    struct dirent *dirent_p;
    while((dirent_p = readdir(dir))!= NULL){
        bzero(&train,sizeof(train));
        train.length = strlen(dirent_p->d_name);
        strcpy(train.data,dirent_p->d_name);
        /* printf("%s ",train.data); */
        send(pclient->fd,&train.length,sizeof(train.length),MSG_NOSIGNAL);
        send(pclient->fd,train.data,train.length,MSG_NOSIGNAL);
    }
    bzero(&train,sizeof(train));
    send(pclient->fd,&train.length,sizeof(train.length),MSG_NOSIGNAL);
    send(pclient->fd,train.data,train.length,MSG_NOSIGNAL);
    closedir(dir);
    return 0;
}

int doLl(client_t *pclient){
    char absPath[1024]={0};
    getPwdFromStack(pclient,absPath,sizeof(absPath));
    DIR *dir = opendir(absPath);
    ERROR_CHECK(dir,NULL,"opendir");

    int ret_ch = chdir(absPath);
    ERROR_CHECK(ret_ch,-1,"chdir");

    /*先遍历一遍查看有多少个文件，再创建多少个结点*/
    int count = 0;
    struct dirent *dirent_p;
    while((dirent_p = readdir(dir))!= NULL){
        ++count;
    }
    struct dirent **dir_arr = (struct dirent**)malloc(sizeof(struct dirent*)*count);
    ERROR_CHECK(dir_arr,NULL,"dir_arr");
    rewinddir(dir);

    /*存入数组中*/
    int idex = 0;
    while((dirent_p = readdir(dir))!= NULL){
        dir_arr[idex] = dirent_p;
        ++idex;
    }
    /*排序*/
    qsort(dir_arr,count,sizeof(struct dirent*),compare);

    long total_size = 0;
    /*循环发送指针数组中文件信息*/
    train_t train;
    for(int i = 0;i < count; ++i){
        struct stat stat_buf;
        int ret = stat(dir_arr[i]->d_name,&stat_buf);
        total_size += stat_buf.st_blocks;
        ERROR_CHECK(ret,-1,"stat");

        char mode_str[11] = {0};
        trans_rwx(stat_buf.st_mode,mode_str);
        char time_str[100] = {0};
        trans_time(stat_buf.st_mtim.tv_sec,time_str);

        char info[4096] = {0};
        sprintf(info,"%s %2lu %s %s %5lu %13s %s",
                mode_str,
                stat_buf.st_nlink,
                getpwuid(stat_buf.st_uid)->pw_name,
                getgrgid(stat_buf.st_gid)->gr_name,
                stat_buf.st_size,
                time_str,
                dir_arr[i]->d_name);
        bzero(&train,sizeof(train));
        train.length = strlen(info);
        strcpy(train.data,info);
        /* printf("%s ",train.data); */
        send(pclient->fd,&train.length,sizeof(train.length),MSG_NOSIGNAL);
        send(pclient->fd,train.data,train.length,MSG_NOSIGNAL);
    }
    char blok[256] = {0};
    sprintf(blok,"总用量 %lu",(total_size * 512)/1024);
    bzero(&train,sizeof(train));
    train.length = strlen(blok);
    strcpy(train.data,blok);
    send(pclient->fd,&train.length,sizeof(train.length),MSG_NOSIGNAL);
    send(pclient->fd,train.data,train.length,MSG_NOSIGNAL);

    //发送退出信号
    bzero(&train,sizeof(train));
    send(pclient->fd,&train.length,sizeof(train.length),MSG_NOSIGNAL);
    send(pclient->fd,train.data,train.length,MSG_NOSIGNAL);
    free(dir_arr);
    closedir(dir);
    return 0;
}

int doMkdir(client_t* pclient,const char *targetName){
    char absPath[1024] = {0};
    getPwdFromStack(pclient, absPath, sizeof(absPath));

    char fullPath[1024] = {0};
    snprintf(fullPath, sizeof(fullPath), "%s/%s", absPath, targetName);

    int ret = mkdir(fullPath, 0755);  //用 mkdir 创建目录
    if(ret == -1){
        perror("mkdir fail");
        return -1;
    }

    train_t train;
    strcpy(train.data, "mkdir success");
    train.length = strlen(train.data);
    send(pclient->fd, &train.length, sizeof(train.length), MSG_NOSIGNAL);
    send(pclient->fd, train.data, train.length, MSG_NOSIGNAL);

    return 0;
}

int doRemove(client_t *pclient, const char *targetName) {
    char absPath[1024] = {0};
    getPwdFromStack(pclient, absPath, sizeof(absPath));

    // 拼接目标路径
    char fullPath[1024] = {0};
    snprintf(fullPath, sizeof(fullPath), "%s/%s", absPath, targetName);

    train_t train;
    int ret = unlink(fullPath);
    if (ret == -1) {
        perror("unlink fail");
        snprintf(train.data, sizeof(train.data), "remove failed: %s", strerror(errno));
    } else {
        snprintf(train.data, sizeof(train.data), "remove success: %s", targetName);
    }

    train.length = strlen(train.data);
    send(pclient->fd, &train.length, sizeof(train.length), MSG_NOSIGNAL);
    send(pclient->fd, train.data, train.length, MSG_NOSIGNAL);

    return ret;
}

int doGets(client_t *pclient, const char *filename) {
    // 防止路径穿越攻击
    if (strstr(filename, "..") != NULL || strchr(filename, '/') != NULL) {
        fprintf(stderr, "[GETS] Invalid filename: %s\n", filename);
        return -1;
    }

    printf("[GETS] sendFile() to client: %s\n", filename);
    sendFile(pclient->fd, (char *)filename);
    return 0;
}

int doPuts(client_t *pclient) {
    // 接收上传文件（客户端上传，服务端接收）
    printf("[PUTS] recvFile() from client\n");
    recvFile(pclient->fd);
    return 0;
}

int workCmd(int netFd)
{
    client_t *pclient = (client_t*)calloc(1,sizeof(client_t));
    pclient->fd = netFd;

    //获取客户端名字
    int nameLength = 0;
    recvn(netFd,&nameLength,sizeof(int));
    recvn(netFd,pclient->userName,nameLength);

    //pwd保存基地址
    //client->pwd 保存客户端最后地址
    char pwd[1024];
    getcwd(pwd,sizeof(pwd));
    initPwdStack(pclient,pwd);
    printf("%s connected.Initial path: %s\n",pclient->userName,pwd);


    while(1){
        printf("Waiting for command from client %s\n",pclient->userName);
        //接受命令
        TLVtrain_t tlv;
        memset(&tlv,0,sizeof(tlv));

        int ret1 = recvn(netFd,&tlv.type,sizeof(tlv.type));
        if (ret1 != 0) {
            printf("[DEBUG-SERVER] recvn type failed: ret1=%d, exiting\n", ret1);
            break;
        }
        printf("[DEBUG-SERVER] ret1=%d, type=%d\n", ret1, tlv.type);
        int ret2 = recvn(netFd,&tlv.length,sizeof(tlv.length));
        if (ret2 != 0) {
            printf("[DEBUG-SERVER] recvn length failed: ret2=%d, exiting\n", ret2);
            break;
        }
        printf("[DEBUG-SERVER] ret2=%d, length=%d\n", ret2, tlv.length);
        if (tlv.length > 0) {
            int ret3 = recvn(netFd,tlv.data,tlv.length);
            if (ret3 != 0) {
                printf("[DEBUG-SERVER] recvn data failed: ret3=%d, exiting\n", ret3);
                break;
            }
            printf("[DEBUG-SERVER] ret3=%d, data=%s\n", ret3, tlv.data);
            tlv.data[tlv.length]='\0';
        }else{
            tlv.data[0] = '\0';
        }
        printf("Command:%d,Data:%s\n", tlv.type, tlv.data);


        /* if(recvn(netFd,&tlv.type,sizeof(tlv.type))<=0)break; */
        /* if(recvn(netFd,&tlv.length,sizeof(tlv.length))<=0)break; */
        /* if(tlv.length>0){ */
        /*     if(recvn(netFd,tlv.data,tlv.length)<=0)break; */
        /*     tlv.data[tlv.length]='\0'; */
        /* }else{ */
        /*     tlv.data[0] = '\0'; */
        /* } */
        printf("Command:%d,Data:%s\n",tlv.type,tlv.data);

        //分析命令
        if(tlv.type == CD){
            doCd(pclient,tlv.data);
        }
        else if(tlv.type == LS){
            doLs(pclient);
        }
        else if(tlv.type == LL){
            doLl(pclient);
        }
        //pwd客户端自己搞
        else if(tlv.type == PUTS){
            doPuts(pclient);
        }
        else if(tlv.type == GETS){
            doGets(pclient,tlv.data);
        }
        else if(tlv.type == REMOVE){
            doRemove(pclient,tlv.data);
        }
        else if(tlv.type == MKDIR){
            doMkdir(pclient,tlv.data);
        }
        else if(tlv.type == RMDIR){
            doRmdir(pclient,tlv.data);
        }
        else if(tlv.type == QUIT || tlv.length == 0){
            printf("Client %s disconnected.\n",pclient->userName);
            destroyPwdStack(pclient);
            free(pclient);
            break;
        }
    }
    return 0;
}

/* 转换权限 */
static void trans_rwx(mode_t mode,char *mode_str)
{
    /* 3*3+1+'\0' */
    //第一个位置是文件格式，先留着
    mode_str[1] = (mode & 0400)? 'r':'-';
    mode_str[2] = (mode & 0200)? 'w':'-';
    mode_str[3] = (mode & 0100)? 'x':'-';
    mode_str[4] = (mode & 0040)? 'r':'-';
    mode_str[5] = (mode & 0020)? 'w':'-';
    mode_str[6] = (mode & 0010)? 'x':'-';
    mode_str[7] = (mode & 0004)? 'r':'-';
    mode_str[8] = (mode & 0002)? 'w':'-';
    mode_str[9] = (mode & 0001)? 'x':'-';

    mode_str[10] = '\0';

    /* 查看inode */
    switch(mode & S_IFMT){
    case S_IFBLK: mode_str[0] = 'b';  break;
    case S_IFCHR: mode_str[0] = 'c';  break;
    case S_IFSOCK: mode_str[0] = 's';  break;
    case S_IFLNK: mode_str[0] = 'l';  break;
    case S_IFDIR: mode_str[0] = 'd';  break;
    case S_IFIFO: mode_str[0] = 'p';  break;
    case S_IFREG: mode_str[0] = '-';  break;
    default:mode_str[0] = '?';break;
    }
}

/* 转换时间 */
static void trans_time(time_t mtime,char *time_str)
{
    struct tm* st_tm = localtime(&mtime);
    sprintf(time_str,"%2d月 %2d %02d:%02d",
            st_tm->tm_mon+1,
            st_tm->tm_mday,
            st_tm->tm_hour,
            st_tm->tm_min);
}

static int compare(const void *a,const void *b)
{
    /*按照字典顺序排序*/
    struct dirent * dir_a = *(struct dirent **)a;
    ERROR_CHECK(dir_a,NULL,"dir_a");
    struct dirent * dir_b = *(struct dirent **)b;
    ERROR_CHECK(dir_b,NULL,"dir_b");
    /* puts("1\n"); */
    return strcasecmp(dir_a->d_name,dir_b->d_name);
}

